from kivy.event import EventDispatcher

from kivy.uix.textinput import TextInput
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout

from kivy.properties import  ListProperty, ObjectProperty, StringProperty

from kivyext.treegridview import TreeViewNode, MyListView
from kivyext.combobox import ComboBox
from kivyext.spinbox import SpinBox
from guiCommon import CompactLabel, CompactTextInput, LeVinTreeLabel, LeVinSpinBox, LeVinCombo
import guiCommon

from world.worldArea import GroupAppellationArea
from world.bottle import Bottle


from levinCommon import dateFromOrdinal, ordinalFromDate, date_today, dateformat
from world.wineObjects import BottleBase, WineObjectTranslation
from world.wineObjects import colors as wineColors

class _NoArea(object):
    pass
NoArea = _NoArea

class AttributeEdit(TreeViewNode):
    attKey = StringProperty(None)

    # list of parameters to pass to the internal edition widget.
    # can be in the form 'param' or ('param', value) 
    edition_parameters = []

    # internal dictionnary representation of the above
    _edition_dict = {}

    previousWid = None
    
    def __init__(self, att, lab="", **kargs):
        if lab=="":
            lab=WineObjectTranslation[att]
        self.label = CompactLabel(text=lab)
        self.extractParam(kargs)
        self.editWid = self.buildEditWid()
        kargs['attKey'] = att
        kargs['columns'] = [ self.label, self.editWid ]
        super(AttributeEdit,self).__init__(**kargs)


        self._post_init()

    def buildEditWid(self):
        """To be implemented in derived classes"""
        pass
    
    def _post_init(self):
        pass

    def extractParam(self,kargs):
        d = dict()
        for p in self.edition_parameters:
            try:
                p, defautlv = p
            except:
                defautlv = None
            v = kargs.get(p,defautlv)
            if v is not None:
                d[p]=v
        self._edition_dict = d


    def bindToNextWid(self, o):
        self.previousWid = o
        self.editWid.bind(on_text_validate = o.editWid.set_focus)

    def bindToPreviousWid(self, prev):
        pass
    
def _set_focus(self,*dummy) :
    self.focus = True
TextInput.set_focus = _set_focus

class AttributeText(AttributeEdit):
    edition_parameters = [('multiline',False) , ('TextInputClass',CompactTextInput) ,'text']

    def buildEditWid(self):
        TextInputClass = self._edition_dict.pop('TextInputClass')
        return TextInputClass(**self._edition_dict)
    def fromEditedObject(self, bottle):
        text = getattr(bottle, self.attKey)
        
        if text is  None:
            text=''
        self.editWid.text = text 

    def toEditedObject(self, bottle):
        text = self.editWid.text.decode('utf-8')
        if text != '':
            setattr( bottle, self.attKey, text)

class DateEdit(AttributeText):
    def toEditedObject(self, bottle):
        setattr( bottle, self.attKey, ordinalFromDate(self.editWid.text))

    def fromEditedObject(self, bottle):
        date = getattr(bottle, self.attKey)
        if date is not None:
            self.editWid.text = dateFromOrdinal(date)
    
class ComboListView(MyListView):
    DefaultWidClass = LeVinTreeLabel

class ComboEdit(AttributeEdit):
    edition_parameters = [ ('editable',True), ('content' ,[] )]

    def buildEditWid(self):
        return LeVinCombo( unlocalize=True, **self._edition_dict)

    def fromEditedObject(self, bottle):
        t = getattr(bottle, self.attKey)
        #print 'fromEditedObject  ', self.attKey, t
        self.editWid.text = t if t else ''

    def toEditedObject(self, bottle):
        t = self.editWid.text
        if not isinstance(t,unicode) : t = t.decode('utf-8')
        setattr( bottle, self.attKey, t)

    def bindToNextWid(self, o):
        self.editWid.bind(on_choice_validated = o.editWid.set_focus)
        print ' bind ', self.editWid, self.attKey, ' to ', o.editWid, o.attKey


class ColorEdit(ComboEdit):
    def fromEditedObject(self, bottle):
        t = getattr(bottle, self.attKey)
        #print 'color ',t
        self.editWid.text = wineColors[t] if t is not None else ''

    def toEditedObject(self, bottle):
        c =  self.editWid.text
        try:
            i = wineColors.index(c)
            setattr( bottle, self.attKey, i)
        except:
            pass


class CepLeVinCombo(LeVinCombo):
    cepEd = None
    def set_focus(self, *l):
        print " TTT CepLeVinCombo !" 
        self.cepEd.retrieve_cepages(clearText=False)
        return super(CepLeVinCombo, self).set_focus(*l)
    def on_press(self):
        self.cepEd.retrieve_cepages(clearText=False)
        return super(CepLeVinCombo,self).on_press()
    
class CepageEdit(ComboEdit):
    colorEdit = None
    appEdit = None

    def __init__(self, att, **kargs):
        super(CepageEdit,self).__init__(att,**kargs)

    def buildEditWid(self):
        c=CepLeVinCombo( unlocalize=True,  ListViewClass=ComboListView, **self._edition_dict)
        c.cepEd = self
        return c
        
    def retrieve_cepages_b(self, clearText=True, *dummy):
        print 'retrieve_cepages_b'
        return self.retrieve_cepages(clearText, *dummy)
    def retrieve_cepages(self, clearText=True, *dummy):
        content = []
        i = self.colorEdit.editWid.selected_index
        print '_____ CepageEdit, ',i
        if i>-1:
            va = self.appEdit.find_valid_area()
            print '_____ CepageEdit, ', va.name , va , self.appEdit
            content = va.cepages(i)
        print '_____ CepageEdit, ',content
        self.editWid.change_content( content  )        
        if clearText:
            self.editWid.text = ''

    def toEditedObject(self, bottle):
        va = self.appEdit.find_valid_area()
        t = self.editWid.text
        i = self.colorEdit.editWid.selected_index
        if not isinstance(t,unicode) : t = t.decode('utf-8')            
        if t!='' and i>-1:
            print " XXXX adding cepage ", t
            va.getRegionOrAbove().addCepages(i,t, True)
        bottle.cepage = t

    def bindToPreviousWid(self, prev):
        # prev is an area !
        print 'Binding ', self, ' to ', prev
        prev.bind( area = self.retrieve_cepages_b)

        
class _TopLevelArea(EventDispatcher):
    area = ObjectProperty(None)
    def find_valid_area(self):
        return self.area
topLevelArea = _TopLevelArea( )

class AreaEdit(ComboEdit):
    edition_parameters = ['editable', 'previousWid']

    area = ObjectProperty(None)

    cepageEdit = None

    forceChildFocus = False
    childW = None
    
    def find_valid_area(self):
        if self.area is not NoArea:
            return self.area
        return self.previousWid.find_valid_area()

    def _post_init(self):
        self.editWid.bind(on_choice_validated = self.choice_validated)
        self.previousWid.bind(area = self.parent_area_changed)
        if self.previousWid is topLevelArea:
            from levinCommon import wineWorld
            self.previousWid.area = wineWorld
        
    def extractParam(self,kargs):
        self.previousWid = kargs.pop('previousWid', topLevelArea)
        super(AreaEdit,self).extractParam(kargs)

    def fromEditedObject(self, bottle):
        areaName = getattr(bottle, self.attKey)
        if areaName:
            self.area = self.previousWid.area[areaName]
            self.editWid.text = areaName

    def choice_validated(self, *dummy):
        areaName = self.editWid.text
        parentArea = self.previousWid.area[areaName]
        parentArea.sortSubAreas()
        self.area = parentArea
        print 'Area choice_validated ', self , areaName, ' focus to ', self.childW
        ## if self.forceChildFocus:
        ##     self.childW.editWid.set_focus()
        ## self.forceChildFocus = False
        self.childW.editWid.set_focus()
        
    def parent_area_changed(self,dummy,a):
        print  '4444   changing content', self, a.name, a, [sa.localname for sa in a.subAreas()]
        self.editWid.change_content( sa.localname for sa in a.subAreas() )        
        self.editWid.text = ''
        self.area = NoArea

    def toEditedObject(self, bottle):        
        oldareaname = getattr( bottle, self.attKey)
        print ' toEditedObject ', self.attKey, ' oldername=', oldareaname, 'new=',self.editWid.text
        setattr( bottle, self.attKey, self.editWid.text)
        return oldareaname != self.editWid.text 


    def childWillNeedToFocus(self,*dummy):
        self.forceChildFocus = True

    def bindToNextWid(self, o):
        self.childW = o        
        ## if isinstance(o, AreaEdit):
        ##     # then o is our child.
        ##     self.childW = o
        ## ##     self.editWid.bind(on_choice_validated = self.childWillNeedToFocus)
        ## ## else:
        ## ##     self.editWid.bind(on_choice_validated = o.editWid.set_focus)
        ## ## #print ' bind ', self.editWid, self.attKey, ' to ', o.editWid, o.attKey


        
class GroupAppLabel(CompactLabel):
    pass

class AppellationEdit(AreaEdit):
    def __init__(self, att, **kargs):
        super(AppellationEdit,self).__init__(att,**kargs)
        defaultCls = self.editWid.entryWidClass
        def appellationComboItemClass(*l,**kl):
            item = kl['text']
            if not isinstance(item,basestring):
                return item
            else:
                return defaultCls(*l,**kl)
        self.editWid.entryWidClass = appellationComboItemClass
        
    def parent_area_changed(self,dummy,a):
        if a is not NoArea: print  '555555   AppellationEdit changing content', self, a.name, a, [sa.localname for sa in a.subAreas()]
        else : print  '555555   AppellationEdit changing content to None'
        newcontent = []
        if a is NoArea :
            newcontent = []
        else:
            for sa in a.subAreas():
                #print 'adding appellation sub nodes ',sa, sa.name
                if isinstance(sa, GroupAppellationArea):
                    # create explicitely the node and set its no_selection flag
                    #n = TreeViewNode( columns=[GroupAppLabel(text=sa.localname if sa.localname else sa.name)], no_selection = True)
                    n = GroupAppLabel(text=sa.localname if sa.localname else sa.name)
                    #n = Label(text=sa.localname if sa.localname else sa.name, size_hint_y=None)
                    newcontent +=[n ]
                    newcontent +=[ ssa.name for ssa in sa.subAreas() ]
                else:
                    newcontent +=[ sa.localname if sa.localname else sa.name ]

        self.editWid.change_content( newcontent)
        self.editWid.text = ''
        self.area = NoArea

    def choice_validated(self, *dummy):
        areaName = self.editWid.text
        area  = self.previousWid.area.getSubAreaRec(areaName)
        print 'App Area choice_validated ', self,  areaName

        if area:            
            self.area = area
        else:
            a = self.previousWid.area[areaName] # this will build the area
            self.area = a
        self.childW.editWid.set_focus()

class SpinBoxEdit(AttributeEdit):
    edition_parameters = [('integer', True) , 'value', 'step']
    defaultV = 0
    def buildEditWid(self):
        self.defaultV = self._edition_dict.get('value',0)
        return LeVinSpinBox(**self._edition_dict)
    
    def fromEditedObject(self, bottle):
        value = getattr(bottle, self.attKey)
        self.editWid.value = value if value is not None else self.defaultV
        
    def toEditedObject(self, bottle):
        setattr( bottle, self.attKey, self.editWid.value)

    def bindToNextWid(self, o):
        self.editWid.bind(on_choice_validated = o.editWid.set_focus)
        
    
    
        


class WineEditWindowContent(GridLayout):
    pass


class EditWidgetBase(object):

    def fromEditedObject(self, bottle):
        self.current_bottle = bottle
        for a in self.editorList.entries:
            a.fromEditedObject(bottle)


    def toEditedObject(self, bottle, moreentries=[]):
        for a in self.editorList.entries:
            if a.toEditedObject(bottle):
                self.someAreaChanged = True
    
class WineEditWidget(EditWidgetBase,BoxLayout):    
    editorList = ObjectProperty(None)


    def __init__(self, **kargs):
        super(WineEditWidget,self).__init__(**kargs)
        self.editorList.DefaultWidClass = LeVinTreeLabel
        self.buildWidgetList()
        
    def buildWidgetList(self):
        self.someAreaChanged=False
        colorEdit = ColorEdit("color"  ,  content = wineColors)
        c = AreaEdit("country",  parent=None, editable=True)
        r = AreaEdit("region",  previousWid = c , editable=True)
        #"groupAppellation", _("groupAppellation")    
        a=AppellationEdit("appellation",  previousWid=r  , editable=True)

        CepageEdit.appEdit  = a
        CepageEdit.colorEdit = colorEdit

        omin=SpinBoxEdit("optimalMin" ,value=date_today.year)
        omax=SpinBoxEdit("optimalMax" ,value=date_today.year)

        editWidgetList =  [ 
            colorEdit,
            c, r, a,
            CepageEdit("cepage" ),
            AttributeText("cru_name"  ),
            SpinBoxEdit("millesime",  value= date_today.year ),
            SpinBoxEdit("volume",  value=75, integer=False ),
            SpinBoxEdit("alcohol",  value=13, integer=False, step=0.5),
            AttributeText("productor" ),
            SpinBoxEdit("numInStock",  value=6),
            SpinBoxEdit("pricePerBottle",  value=5, integer=False),
            DateEdit("acquireDate",  text=date_today.strftime(dateformat)),
            AttributeText("acquireComment" ),
            AttributeText("geolocalisation" ),
            AttributeText("otherInfo",  multiline=True, TextInputClass=TextInput),
            
            AttributeText("suggestedFrom" ),
            AttributeText("suggestedTo" ),
            omin,
            omax,
            ]

        # Make so optimal max is always > opitmal min
        # bind this function : 
        def setOptMaxOnMinUpdated(*l):
            if omin.editWid.value > omax.editWid.value:
                omax.editWid.value = omin.editWid.value
        omin.editWid.bind(value = setOptMaxOnMinUpdated)

        for a in editWidgetList:
            self.editorList.add_entry(a)
            self.current_bottle = None

        # bind to next widget
        for i in xrange(len(editWidgetList)-1):
            editWidgetList[i].bindToNextWid(editWidgetList[i+1])
        for i in xrange(1,len(editWidgetList)):
            editWidgetList[i].bindToPreviousWid(editWidgetList[i-1])

        
    def start_edit_bottle(self,ev,bottle=None):
        if bottle is None:
            bottle = Bottle()
        theWineEditWindow.content.fromEditedObject(bottle)
        self.editorList.scroller.scroll_y = 1.

        theWineEditWindow.open()

    def end_edit_bottle(self,ev ,bottle,save=False, addNew=False):
        if bottle:
            newbottle = bottle.bottleId == 0
            
            if  save:
                from levin.levinCommon import wineWorld
                theWineEditWindow.content.toEditedObject(bottle)
                if not newbottle and self.someAreaChanged:
                    wineWorld.removeWine(bottle)
                if  self.someAreaChanged:
                    print 'ADDING a botle !'
                    self.someAreaChanged = False
                    wineWorld.addWine(bottle)
                else:
                    bottle.update_widget()

                bottle.requestSaveContent()
                guiCommon.commonEvents.dispatch('on_bottle_edited', bottle)

            
        if addNew:
            self.start_edit_bottle(ev)
        else:
            theWineEditWindow.dismiss()
            print ' DISMISSED'



from kivy.factory import Factory
for cls in [WineEditWidget, GroupAppLabel]:
    Factory.register(cls.__name__, cls=cls)
from kivy.lang import Builder
guiCommon.load_kv_file('levinpopup.kv')
guiCommon.load_kv_file('wineEdit.kv')


theWineEditWindow = guiCommon.LeVinPopup(title=_('Edit Bottle'),  size_hint=(None,None), size=(500,500))
theWineEditWindow.content = WineEditWidget()
    
guiCommon.commonEvents.bind(on_start_edit_bottle = theWineEditWindow.content.start_edit_bottle    )
guiCommon.commonEvents.bind(on_end_edit_bottle   = theWineEditWindow.content.end_edit_bottle    )
